#pragma once

#include "CpUtil.h"
#include "Tokenizer.h"
#include <assert.h>

namespace CppClean
{

enum MemberVisibility
{
	VISIBILITY_NONE,
	VISIBILITY_PUBLIC, 
	VISIBILITY_PROTECTED, 
	VISIBILITY_PRIVATE,
};

enum FunctionType
{
	FUNCTION_NONE = 0x00,
	FUNCTION_CONST = 0x01,
	FUNCTION_VIRTUAL = 0x02,
	FUNCTION_PURE_VIRTUAL = 0x04,
	FUNCTION_CTOR = 0x08,
	FUNCTION_DTOR = 0x10,
	FUNCTION_ATTRIBUTE = 0x20,
	FUNCTION_UNKNOWN_ANNOTATION = 0x40,
	FUNCTION_THROW = 0x80,
};

class CNodeBase : public CCpObject
{
public:
	CNodeBase(int start, int end)
		:m_nStart(start), m_nEnd(end){}
	virtual ~CNodeBase(){}

public:
	virtual BOOL IsDeclaration() {return FALSE;}
	virtual BOOL IsDefinition() {return FALSE;}
	virtual BOOL IsExportable() {return FALSE;}
	virtual BOOL Requires(CNodeBase* pNode){return FALSE;}
	virtual string GetName(){ return "node"; }
	virtual string __name() { return "CNodeBase"; }
	virtual string GetString()
	{
		return _StringHelper(NULL);
	}
	virtual string _StringHelper(LPCSTR szSuffix)
	{
		string name = __name();
		char* suffix = szSuffix ? szSuffix : "";

		char szOutput[MAX_STRING_LEN] = {0};
#ifdef _DEBUG
		_snprintf_s(szOutput, MAX_STRING_LEN - 1, "%s(%d, %d, %s)", name.c_str(), m_nStart, m_nEnd, suffix);
#else
		_snprintf_s(szOutput, MAX_STRING_LEN - 1, "%s(%s)", name.c_str(), suffix);
#endif
		return string(szOutput);
	}

protected:
	int m_nStart;
	int m_nEnd;
};

class CDefine :	public CNodeBase
{
public:
	CDefine(int start, int end, LPCSTR name, LPCSTR definition)
		:CNodeBase(start, end), m_sDefinition(definition), m_sName(name) {}
	virtual ~CDefine(void) {}

public:
	virtual string GetString()
	{
		string suffix = m_sName;
		suffix += " ";
		suffix += m_sDefinition;
		return _StringHelper(suffix.c_str());
	}
	virtual string __name() { return "Define"; }

protected:
	string m_sName;
	string m_sDefinition;
};

class CInclude : public CNodeBase
{
public:
	CInclude(int start, int end, LPCSTR filename, BOOL bSys)
		:CNodeBase(start, end), m_bSys(bSys), m_sFileName(filename) {}
	virtual ~CInclude(void){}

public:
	virtual string __name() { return "Include"; }
	virtual string GetString()
	{
		string fmt;
		if(!m_bSys)
			fmt = "\"" + m_sFileName + "\"";
		else
			fmt = "<" + m_sFileName + ">";
		return _StringHelper(fmt.c_str());
	}

protected:
	BOOL m_bSys;
	string m_sFileName;
};

class CGoto : public CNodeBase
{
public:
	CGoto(int start, int end, LPCSTR label)
		:CNodeBase(start, end), m_sLabel(label) {}
	virtual ~CGoto() {}

public:
	virtual string GetString()	{	return _StringHelper(m_sLabel.c_str());	}
	virtual string __name() { return "Goto"; }

protected:
	string m_sLabel;
};

class CExpr : public CNodeBase
{
public:
	CExpr(int start, int end, LPCSTR expr)
		:CNodeBase(start, end), m_sExpr(expr) {}
	virtual ~CExpr() {}

public:
	virtual string GetString() {	return _StringHelper(m_sExpr.c_str()); }
	virtual string __name() { return "Expression"; }

protected:
	string m_sExpr;
};

class CReturn : public CExpr
{
public:
	CReturn(int start, int end, LPCSTR expr) 
		: CExpr(start, end, expr){}
	virtual ~CReturn() {}

public:
	virtual string __name() { return "Return"; }
	virtual string GetString()
	{
		return _StringHelper(m_sExpr.c_str());
	}
};

class CDelete : public CExpr
{
public:
	CDelete(int start, int end, LPCSTR expr) 
		: CExpr(start, end, expr){}
	virtual ~CDelete() {}

public:
	virtual string __name() { return "Delete"; }
	virtual string GetString()
	{
		return _StringHelper(m_sExpr.c_str());
	}
};

class CFriend : public CExpr
{
public:
	CFriend(int start, int end, LPCSTR expr, const STRINGLIST& np) 
		: CExpr(start, end, expr), m_lNameSpace(np.begin(), np.end()) {	}
	virtual ~CFriend() {}

public:
	virtual string __name() { return "Friend"; }
	virtual string GetString()
	{
		return _StringHelper(m_sExpr.c_str());
	}

protected:
	STRINGLIST m_lNameSpace;
};

class CUsing : public CNodeBase
{
public:
	CUsing(int start, int end, LPCSTR name) 
		: CNodeBase(start, end), m_sName(name){}
	virtual ~CUsing() {}

public:
	virtual string GetString()
	{
		return _StringHelper(m_sName.c_str());
	}
	virtual string __name() { return "Using"; }

protected:
	string m_sName;
};

class CGenericDeclaration : public CNodeBase
{
public:
	CGenericDeclaration(int start, int end, LPCSTR name, const STRINGLIST& np)
		:CNodeBase(start, end), m_sName(name), m_lNameSpace(np.begin(), np.end()){}
	virtual ~CGenericDeclaration() {}

public:
	virtual string __name() { return "GenericDeclaration"; }
	virtual string GetName()
	{
		return m_sName;
	}

	virtual string GetFullName()
	{
		string sRet = CStringUtil::Join(m_lNameSpace, "::", "<anonymous>");
		sRet += "::";
		sRet += m_sName;
		return sRet;
	}

	virtual string _TypeStringHelper(LPCSTR szSuffix)
	{
		string suffix(szSuffix);
		STRINGLIST::iterator it = m_lNameSpace.begin();
		if(it != m_lNameSpace.end())
		{
			suffix += " in ";
			suffix += CStringUtil::Join(m_lNameSpace, "::", "<anonymous>");
		}
		return _StringHelper(suffix.c_str());
	}
protected:
	string m_sName;
	STRINGLIST m_lNameSpace;
};

class CType : public CGenericDeclaration
{
public:
	typedef std::deque<CCpAutoPtr<CType> > TYPE_LIST;

	CType(int start, int end, LPCSTR name, const TYPE_LIST& templated_types, const STRINGLIST& modifiers,
		BOOL bReference, BOOL bPointer, BOOL bArray, const STRINGLIST& templ_subs = STRINGLIST())
		:CGenericDeclaration(start, end, name, STRINGLIST()),m_bReference(bReference),
		m_bPointer(bPointer),m_bArray(bArray),m_lModifiers(modifiers.begin(), modifiers.end()),
		m_lTemplateTypes(templated_types.begin(), templated_types.end()), 
		m_lTemplSubDefines(templ_subs.begin(), templ_subs.end())
	{
		if(!name && !m_lModifiers.empty())
		{
			m_sName = m_lModifiers.front();
			m_lModifiers.pop_front();
		}
	}

	virtual ~CType() 
	{
		m_lTemplateTypes.clear();
//		CDbgUtil::myDebugOutputA("CType::~CType", TRUE);
	}

public:
	virtual string __name() { return "Type"; }
	virtual string GetString()
	{
		string prefix;
		if(!m_lModifiers.empty())
		{
			prefix = CStringUtil::Join(m_lModifiers, " ", " ");
			prefix += " ";
		}
		prefix += m_sName;
		if(!m_lTemplateTypes.empty())
		{
			prefix += "<";
			prefix += CStringUtil::JoinObject(m_lTemplateTypes, ",", " ");
			prefix += ">";
		}
		if(!m_lTemplSubDefines.empty())
		{
			prefix += "::";
			prefix += CStringUtil::Join(m_lTemplSubDefines, "::", "");
		}
		if(m_bReference)
			prefix += '&';
		if(m_bPointer)
			prefix += '*';
		if(m_bArray)
			prefix += "[]";

		return _TypeStringHelper(prefix.c_str());
	}
	const TYPE_LIST& GetTemplateTypes(){ return m_lTemplateTypes; }
	const STRINGLIST& GetModifier(){return m_lModifiers;}

protected:
	TYPE_LIST m_lTemplateTypes;
	STRINGLIST m_lModifiers;
	STRINGLIST m_lTemplSubDefines;
	BOOL m_bReference;
	BOOL m_bPointer;
	BOOL m_bArray;
};

class CParameter : public CNodeBase
{
public:
	typedef std::deque<CCpAutoPtr<CParameter> > PARAM_LIST;

	CParameter(int start, int end, LPCSTR name, const CCpAutoPtr<CType>& param_type, const CToken::TOKEN_LIST& defaultval)
		:CNodeBase(start, end), m_sName(name),m_Type(param_type),
		m_dqDefault(defaultval.begin(), defaultval.end()){}
	virtual ~CParameter(){}

public:
	virtual string __name() { return "Parameter"; }
	virtual string GetString()
	{
		string suffix;
		if(m_Type)
			suffix = m_Type->GetString();
		suffix += " ";
		suffix += m_sName;
		if(!m_dqDefault.empty())
		{
			suffix += '=';
			suffix += CStringUtil::JoinObjectName(m_dqDefault, "", "");
		}
		return _StringHelper(suffix.c_str());
	}

	virtual BOOL Requires(CNodeBase* pNode)
	{
		CGenericDeclaration* pDeclar = dynamic_cast<CGenericDeclaration*>(pNode);
		if(pDeclar != NULL && m_Type != NULL)
		{
			return 0 == (pDeclar->GetName()).compare(m_Type->GetName());
		}
		return FALSE;
	}

protected:
	string m_sName;
	CToken::TOKEN_LIST m_dqDefault;
	CCpAutoPtr<CType> m_Type;
};

class CVariableDeclaration : public CGenericDeclaration
{
public:
	CVariableDeclaration(int start, int end, LPCSTR name, const CPOBJECT& var_type, 
		LPCSTR initial_val, const STRINGLIST& np)
		:CGenericDeclaration(start, end, name, np),m_Type((CType*)var_type.Get()), m_sValue(initial_val) {}
	virtual ~CVariableDeclaration() {}
public:
	virtual BOOL Requires(CNodeBase* pNode)
	{
		CGenericDeclaration* pDeclar = dynamic_cast<CGenericDeclaration*>(pNode);
		if(pDeclar != NULL && m_Type != NULL)
		{
			return 0 == (pDeclar->GetName()).compare(m_Type->GetName());
		}
		return FALSE;
	}

	virtual string __name() { return "VariableDeclare"; }
	virtual string GetString()
	{
		string suffix;
		if(m_Type != NULL)
			suffix = m_Type->GetString();
		suffix += " ";
		suffix += m_sName;
		if(!m_sValue.empty())
		{
			suffix += '=';
			suffix += m_sValue;
		}
		return _StringHelper(suffix.c_str());
	}

protected:
	string m_sValue;
	CCpAutoPtr<CType> m_Type;
};

class CTypedef : public CGenericDeclaration
{
public:
	CTypedef(int start, int end, LPCSTR name, const OBJECTLIST& alias, const STRINGLIST& np)
		:CGenericDeclaration(start, end, name, np), m_pAlias(alias.begin(), alias.end())
	{
	}

	virtual ~CTypedef() 
	{
	}

public:
	virtual string __name() { return "Typedef"; }
	virtual string GetString()
	{
		string suffix = m_sName;
		suffix += CStringUtil::JoinObject(m_pAlias, ",", "");
		return _TypeStringHelper(suffix.c_str());
	}
	virtual BOOL IsDefinition()
	{
		return TRUE;
	}
	virtual BOOL IsExportable()
	{
		return TRUE;
	}
	virtual BOOL Requires(CNodeBase* pNode)
	{
		if(pNode == NULL)
			return FALSE;

		string sName = pNode->GetName();
		OBJECTLIST::iterator it = m_pAlias.begin();
		for(; it != m_pAlias.end(); it++)
		{
			CPOBJECT& oVal = *it;
			string sItName = oVal->GetName();
			if(!sItName.empty() && sItName == sName)
				return TRUE;
		}
		return FALSE;
	}

protected:
	OBJECTLIST m_pAlias;
};

class CNestedType : public CGenericDeclaration
{
public:
	CNestedType(int start, int end, LPCSTR name, const OBJECTLIST& fields, const STRINGLIST& np)
		: CGenericDeclaration(start, end, name, np),m_pFields(fields.begin(), fields.end())	{ }
	virtual ~CNestedType() {}

public:
	virtual string __name() { return "NestedType"; }
	virtual string GetString()
	{
		string suffix = m_sName;
		suffix += ", {";
		suffix += CStringUtil::JoinObject(m_pFields, ",", "");
		suffix += "}";
		return _TypeStringHelper(suffix.c_str());
	}
	virtual BOOL IsDefinition()
	{
		return TRUE;
	}
	virtual BOOL IsExportable()
	{
		return TRUE;
	}

protected:
	OBJECTLIST m_pFields;
};

class CEnum : public CNestedType
{
public:
	CEnum(int start, int end, LPCSTR name, const OBJECTLIST& fields, const STRINGLIST& np)
		:CNestedType(start, end, name, fields, np) {}
	virtual ~CEnum();

public:
	virtual string __name()	{	return "Enum"; }
};

class CUnion : public CNestedType
{
public:
	CUnion(int start, int end, LPCSTR name, const OBJECTLIST& fields, const STRINGLIST& np)
		:CNestedType(start, end, name, fields, np) {}
	virtual ~CUnion();

public:
	virtual string __name()	{	return "Union"; }
};

class CClass : public CGenericDeclaration
{
public:
	CClass(int start, int end, LPCSTR name, const OBJECTLIST& body, const OBJECTLIST& templates, 
		const STRINGLIST& np, const OBJECTLIST& bases)
		: CGenericDeclaration(start, end, name, np), m_pBody(body.begin(), body.end()),
		m_Bases(bases.begin(), bases.end()), m_pTemplates(templates.begin(), templates.end()) {}
	virtual ~CClass() {}

public:
	virtual BOOL IsDeclaration()
	{
		return (m_Bases.empty() && m_pBody.empty());
	}
	virtual BOOL IsDefinition()
	{
		return !IsDeclaration();
	}
	virtual BOOL IsExportable()
	{
		return !IsDeclaration();
	}
	virtual string __name() { return "Class"; }
	virtual string GetString()
	{
		string suffix;
		suffix = m_sName;
		if(!m_pTemplates.empty())
		{
			suffix += '<';
			suffix += CStringUtil::JoinObjectName(m_pTemplates, ",", "");
			suffix += '>';
		}
		suffix += ",";
		suffix += CStringUtil::JoinObject(m_Bases, ",", "None");
		suffix += ',';
		suffix += CStringUtil::JoinObject(m_pBody, ",", "");

		return _TypeStringHelper(suffix.c_str());
	}

	//TODO, here the code is not correct, change it latter.
	virtual BOOL Requires(CNodeBase* pNode)
	{
		if(m_Bases.empty())
			return FALSE;
		OBJECTLIST::iterator it = m_Bases.begin();
		for(; it != m_Bases.end(); it++)
		{
			CType* pType = dynamic_cast<CType*>(it->Get());
			if(pType == NULL)
				continue;
			if(pType->GetName().compare(pNode->GetName()) == 0)
				return TRUE;
		}
		return FALSE;
	}
protected:
	OBJECTLIST m_pBody;
	OBJECTLIST m_pTemplates;
	OBJECTLIST m_Bases;
};

class CFunction : public CGenericDeclaration
{
public:
	typedef std::deque<CCpAutoPtr<CParameter> > PARAM_LIST;
	CFunction(int start, int end, LPCSTR name, const CToken::TOKEN_LIST& return_type,
		const CToken::TOKEN_LIST& parameters, LONG modifiers, const OBJECTLIST& templates,
		const OBJECTLIST& body, const STRINGLIST& np)
		:CGenericDeclaration(start, end, name, np), m_nModifiers(modifiers), 
		m_pBody(body.begin(), body.end()), m_pTemplates(templates.begin(), templates.end())
	{
		//TODO
	}
	virtual ~CFunction() {}

public:
	virtual BOOL IsDeclaration()
	{
		return m_pBody.empty();
	}
	virtual BOOL IsDefinition()
	{
		return !IsDeclaration();
	}
	virtual BOOL IsExportable()
	{
		if(m_ReturnType != NULL)
		{
			BOOL bStatic = FALSE;
			STRINGLIST oModifiers = m_ReturnType->GetModifier();
			STRINGLIST::iterator it = oModifiers.begin();
			for(; it != oModifiers.end(); it++)
			{
				if(*it == "static")
				{
					bStatic = TRUE;
					break;
				}
			}
			if(bStatic)
				return FALSE;
		}
		//TODO
		return !m_lNameSpace.empty();
	}
	virtual string __name() { return "Function"; }
	virtual string GetString()
	{
		string suffix;
		if(m_ReturnType != NULL)
			suffix = m_ReturnType->GetString();
		suffix += ' ';
		suffix += m_sName;
		suffix += '(';
		suffix += CStringUtil::JoinObject(m_Parameters, ",", "");
		suffix += "), ";
		suffix += CStringUtil::IntToXString(m_nModifiers);
		suffix += ", ";
		suffix += CStringUtil::JoinObject(m_pBody, ",", "");

		return _TypeStringHelper(suffix.c_str());
	}

	virtual BOOL Requires(CNodeBase* pNode)
	{
		if(!m_Parameters.empty() && pNode != NULL)
		{
			PARAM_LIST::iterator it = m_Parameters.begin();
			for(; it != m_Parameters.end(); it++)
			{
				CParameter* pParam = it->Get();
				if(pParam && pParam->GetName().compare(pNode->GetName()) == 0)
					return TRUE;
			}
		}
		return FALSE;
	}

protected:
	CCpAutoPtr<CType> m_ReturnType;
	PARAM_LIST m_Parameters;
	LONG m_nModifiers;
	OBJECTLIST m_pBody;
	OBJECTLIST m_pTemplates;
};

class CMethod : public CFunction
{
public:
	CMethod(int start, int end, LPCSTR name, BOOL inclass, const CToken::TOKEN_LIST& return_type,
		const CToken::TOKEN_LIST& parameters, LONG modifiers, const OBJECTLIST& templates,
		const OBJECTLIST& body, const STRINGLIST& np)
		: CFunction(start,end,name,return_type, parameters,modifiers,templates, body,np),m_bInClass(inclass) { }
	virtual ~CMethod() {}

public:
	virtual string __name() { return "Method"; }

protected:
	BOOL m_bInClass;
};

class CTypeConverter : public CCpObject
{
public:
	CTypeConverter(const STRINGLIST& np)
		: m_lNameSpace(np) {}
	virtual ~CTypeConverter() {}
public:
	int GetTemplateEnd(const CToken::TOKEN_LIST& tokens, int start, CToken::TOKEN_LIST& templs);
	void ToType(const CToken::TOKEN_LIST& tokens, CType::TYPE_LIST& results);
	void ToParameter(const CToken::TOKEN_LIST& tokens, CParameter::PARAM_LIST& results);
	void DeclarationToParts(CToken::TOKEN_LIST& aParts, BOOL bRemoveName, string& name, string& sTypeName,
		CType::TYPE_LIST& templTypes, STRINGLIST& aModifiers, CToken::TOKEN_LIST& aDefaults, 
		CToken::TOKEN_LIST& aUnusedTokens, STRINGLIST& templSubs);
	CCpAutoPtr<CType> CreateReturnType(CToken::TOKEN_LIST& aTokens);
	void GetTemplateIndices(const CToken::TOKEN_LIST& aTokens, int& start, int& end);

private:
	void AddType(CToken::TOKEN_LIST& aNameTokens, CType::TYPE_LIST& templTypes, BOOL bReference, BOOL bPointer, 
		BOOL bArray, CType::TYPE_LIST& results, STRINGLIST& aTemplSubs = STRINGLIST());
	void AddParameter(CCpAutoPtr<CToken> pFirstToken, CToken::TOKEN_LIST& aTypeModifiers, CToken::TOKEN_LIST& aDefaults, BOOL bReference, 
		BOOL bPointer, BOOL bArray, CParameter::PARAM_LIST& results);

protected:
	STRINGLIST m_lNameSpace;
};

}
